package nakedb;


import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.Set;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import nakedb.GraphViewToolTipSimple.FitOverviewListener;
import nakedb.GraphViewToolTipSimple.DemoControlToolTip;
import prefuse.Constants;
import prefuse.Display;
import prefuse.Visualization;
import prefuse.action.ActionList;
import prefuse.action.ItemAction;
import prefuse.action.RepaintAction;
import prefuse.action.animate.ColorAnimator;
import prefuse.action.assignment.ColorAction;
import prefuse.action.assignment.DataColorAction;
import prefuse.action.assignment.DataShapeAction;
import prefuse.action.assignment.DataSizeAction;
import prefuse.action.assignment.StrokeAction;
import prefuse.action.filter.GraphDistanceFilter;
import prefuse.data.Graph;
import prefuse.data.Table;
import prefuse.data.Tuple;
import prefuse.data.event.TupleSetListener;
import prefuse.data.query.SearchQueryBinding;
import prefuse.data.search.PrefixSearchTupleSet;
import prefuse.data.search.SearchTupleSet;
import prefuse.data.tuple.TupleSet;
import prefuse.render.DefaultRendererFactory;
import prefuse.render.EdgeRenderer;
import prefuse.render.LabelRenderer;
import prefuse.util.ColorLib;
import prefuse.util.FontLib;
import prefuse.util.ui.JFastLabel;
import prefuse.util.ui.JSearchPanel;
import prefuse.util.ui.JValueSlider;
import prefuse.visual.AggregateItem;
import prefuse.visual.VisualGraph;
import prefuse.visual.VisualItem;

/**
 * Sidebar for NakeDB
 */
public class Sidebar extends JPanel implements ItemListener {
  public boolean showName = true;
  public boolean showColors = true;
  private Visualization m_vis;
  
  private DemoControlToolTip toolTips;
  
  private ActionList draw;
  private EdgeRenderer m_edgeRenderer;
  
  private final NodeSizeAction dataSizeAction;
  private NodeShapeAction nodeShapeAction;
  
  private final JValueSlider wslider;
  
  //GUI components
  private String layoutSelection[] = new String[]{"Force Directed", "Radial Tree", "Circular", "Node Link"};
  private String distortionSelection[] = new String[]{"Default", "Free to move", "Fisheye", "Bifocal"};
  private String encodingSelection[] = new String[]{"None", "Grouping", "Size of Table", "In Connections", "Out Connections"};
  private JCheckBox showNameButton, showToolTipsButton, showSizeButton, showShapeButton, showArrowButton, showAggregateButton;
  private JComboBox layoutCombo, distortionCombo, nodeColorCombo, borderColorCombo, borderThicknessCombo, shapeCombo;
  private JLabel layoutLabel, distortionLabel, nodeColorLabel, borderColorLabel, borderThicknessLabel, shapeLabel;
  private String g, e;
  
  private static LabelShapeRenderer labelRenderer;
  
  private ColorAction nodeStrokeColor;
  private ColorAction aFill; 
  private ColorAction aStroke;
  
  private Set<AggregateItem> aggregateItems;
  
  public Sidebar(NakeDB nakedb, Visualization m_vis_l, Display display, 
      LabelShapeRenderer labelRenderer, EdgeRenderer m_edgeRenderer, String label, String graph, String nodes,  
      String edges, final GraphDistanceFilter filter, int hops, JFastLabel title,
      Set<AggregateItem> aggregateItems) {
    
    Sidebar.labelRenderer = labelRenderer;
    this.m_edgeRenderer = m_edgeRenderer;
    this.aggregateItems = aggregateItems;
    this.setLayout(null);
    m_vis = m_vis_l;
    g = graph;
    e = edges;

    JPanel showSearchPanel = new JPanel();
    showSearchPanel.setBackground(Color.white);
    showSearchPanel.setLayout(new FlowLayout());
    showSearchPanel.setSize(310, 180);
    showSearchPanel.setLocation(0, 0);
    showSearchPanel.setBorder(BorderFactory.createTitledBorder("General"));

    // second box
    JComboBox labelsCombobox = new JComboBox(new String[] { "No text", "Names",
        "Table Size", "Attributes" });
    labelsCombobox.setSelectedIndex(1);
    labelsCombobox.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        JComboBox cb = (JComboBox) e.getSource();
        String action = (String) cb.getSelectedItem();
        if (action.equals("No text")){
        	Sidebar.labelRenderer.setTextField("dummy");
        } else if (action.equals("Names")){
        	Sidebar.labelRenderer.setTextField("name");
        } else if (action.equals("Table Size")){
        	Sidebar.labelRenderer.setTextField(VisualItem.LABEL);
        } else if (action.equals("Attributes")){
        	Sidebar.labelRenderer.setTextField("attributs");
        }
        m_vis.run("draw");
      }
    });
    labelsCombobox.setVisible(true);

    JPanel labelPanel = new JPanel();
    labelPanel.setLayout(new FlowLayout());
    labelPanel.setBackground(Color.white);
    labelPanel.setSize(310, 50);
    Box secondBox = new Box(BoxLayout.X_AXIS);
    secondBox.setMaximumSize(new Dimension(300, 30));
    secondBox.add(new JLabel("Label:   "));
    //secondBox.add(labelsCombobox);
    labelPanel.add(secondBox);
    labelPanel.setBorder(BorderFactory.createTitledBorder("Choose a type of label"));
    labelPanel.setLocation(0, 280);
    labelPanel.add(Box.createVerticalStrut(5));
    
    JPanel distancePanel = new JPanel();
    distancePanel.setBackground(Color.white);
    distancePanel.setLayout(new FlowLayout());
    distancePanel.setSize(310, 80);
    distancePanel.setLocation(0, 180);
    distancePanel.setBorder(BorderFactory.createTitledBorder("Connection to Node"));   

        
    JPanel miniDisplayPanel = new JPanel();
    miniDisplayPanel.setBackground(Color.white);
    miniDisplayPanel.setLayout(new FlowLayout());
    miniDisplayPanel.setSize(310, 290);
    miniDisplayPanel.setLocation(0, 420);
    miniDisplayPanel.setBorder(BorderFactory.createTitledBorder("Mini Display"));
            
    
    layoutLabel = new JLabel("                 Layout ");
    layoutCombo = new JComboBox(layoutSelection);
    layoutCombo.setSelectedIndex(0);
    layoutCombo.addItemListener(this);
    layoutCombo.setVisible(true);
    
    layoutCombo.addActionListener(nakedb);
    
    distortionLabel = new JLabel("          Distortion ");
    
    distortionCombo = new JComboBox(distortionSelection);
    distortionCombo.setSelectedIndex(0);
    distortionCombo.addItemListener(this);
    distortionCombo.setVisible(true);
    
    distortionCombo.addActionListener(nakedb);
    
    nodeColorLabel = new JLabel("Node Color          ");
    
    nodeColorCombo = new JComboBox(encodingSelection);
    nodeColorCombo.setSelectedIndex(0);
    nodeColorCombo.addItemListener(this);
    nodeColorCombo.setVisible(true);
    
    borderColorLabel = new JLabel("Border Color        ");
    
    borderColorCombo = new JComboBox(encodingSelection);
    borderColorCombo.setSelectedIndex(0);
    borderColorCombo.addItemListener(this);
    borderColorCombo.setVisible(true);
    
    borderThicknessLabel = new JLabel("Border Thickness");

    borderThicknessCombo = new JComboBox(encodingSelection);
    borderThicknessCombo.setSelectedIndex(0);
    borderThicknessCombo.addItemListener(this);
    borderThicknessCombo.setVisible(true);
    
    shapeLabel = new JLabel("Shape                  ");
    
    shapeCombo = new JComboBox(encodingSelection);
    shapeCombo.setSelectedIndex(0);
    shapeCombo.addItemListener(this);
    shapeCombo.setVisible(true);

    showNameButton = new JCheckBox("Show Name");
    showNameButton.setSelected(true);
    showNameButton.addItemListener(this);   
    
    showToolTipsButton = new JCheckBox("Show Tooltips");
    showToolTipsButton.addItemListener(this);     
   
    showShapeButton = new JCheckBox("Activate Shape");
    showShapeButton.addItemListener(this);    
    
    showSizeButton = new JCheckBox("Activate Size");
    showSizeButton.setSelected(true);
    showSizeButton.addItemListener(this);   
    
    showArrowButton = new JCheckBox("Activate Arrow");
    showArrowButton.addItemListener(this);  
    
    showAggregateButton = new JCheckBox("Activate Aggregate");
    showAggregateButton.setSelected(false);
    showAggregateButton.addItemListener(this);      
    
    
    
    // Search 
    // animate paint change
    ActionList animatePaint = new ActionList(4000);
    animatePaint.add(new ColorAnimator(nodes));
    animatePaint.add(new RepaintAction());
    m_vis.putAction("animatePaint", animatePaint);
    
    SearchTupleSet search = new PrefixSearchTupleSet();
    m_vis.addFocusGroup(Visualization.SEARCH_ITEMS, search);
    search.addTupleSetListener(new TupleSetListener() {
      public void tupleSetChanged(TupleSet t, Tuple[] add, Tuple[] rem) {
        m_vis.cancel("animatePaint");
        m_vis.run("recolor");
        m_vis.run("draw");
      }
    });
        
    // create the search query binding
    SearchQueryBinding searchQ = new SearchQueryBinding((Table) m_vis
            .getGroup(nodes), label, (SearchTupleSet) m_vis
            .getGroup(Visualization.SEARCH_ITEMS));
            
    JSearchPanel searcher = searchQ.createSearchPanel();
    searcher.setShowResultCount(true);
    searcher.setLabelText(" Find ");
    searcher.setFont(FontLib.getFont("Heltevica", Font.PLAIN, 11));
    

    final JValueSlider slider = new JValueSlider("     Scaling  ", 0, hops, hops);
    slider.addChangeListener(new ChangeListener() {
        public void stateChanged(ChangeEvent e) {
            filter.setDistance(slider.getValue().intValue());
            m_vis.run("draw");
        }
    });
    slider.setBackground(Color.WHITE);
    slider.setValue(10);
    slider.setPreferredSize(new Dimension(280,30));
    slider.setMaximumSize(new Dimension(300,30));
    
    Box cf = new Box(BoxLayout.Y_AXIS);
    cf.add(slider);
    
    // Weigh size    
    dataSizeAction = new NodeSizeAction(nodes, "lines");
    
    JPanel weightPanel = new JPanel();
    weightPanel.setBackground(Color.white);
    weightPanel.setLayout(new FlowLayout());
    weightPanel.setSize(310, 95);
    weightPanel.setLocation(0, 260);
    weightPanel.setBorder(BorderFactory.createTitledBorder("Visual Size"));
    
    wslider = new JValueSlider("     Scaling", 1, 20, 2);
    wslider.addChangeListener(new ChangeListener() {
      public void stateChanged(ChangeEvent e) {
        dataSizeAction.setMaximumSize(wslider.getValue().doubleValue());
        m_vis.run("draw");
      }
    });
    dataSizeAction.setMaximumSize(7.0);
    wslider.setValue(1);
    wslider.setBackground(Color.WHITE);
    wslider.setPreferredSize(new Dimension(280, 30));
    wslider.setMaximumSize(new Dimension(280, 30));
    weightPanel.add(showSizeButton);
    JComboBox sizesActionCombobox = new JComboBox(new String[] { 
        	"table type", "table size", "critical", "repair", "traffic" });
        sizesActionCombobox.setSelectedIndex(1);
        sizesActionCombobox.addActionListener(new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            JComboBox cb = (JComboBox) e.getSource();
            String action = (String) cb.getSelectedItem();
            if (action.equals("table size")){
                dataSizeAction.setDataField("lines");
            } else if (action.equals("table type")){
                dataSizeAction.setDataField("type");
            } else if (action.equals("critical")){
              dataSizeAction.setDataField("critical");
            } else if (action.equals("repair")){
              dataSizeAction.setDataField("repair");
            } else if (action.equals("traffic")){
              dataSizeAction.setDataField("traffic");
            }
            m_vis.run("draw");
          }
        });   
    weightPanel.add(sizesActionCombobox);
    

    
    
    Box df = new Box(BoxLayout.Y_AXIS);
    df.add(wslider);
    weightPanel.add(df);
    


    // Shape
    nodeShapeAction = new NodeShapeAction(nodes, "lines");
    
    JPanel shapePanel = new JPanel();
    shapePanel.setBackground(Color.white);
    shapePanel.setLayout(new FlowLayout());
    shapePanel.setSize(310, 70);
    shapePanel.setLocation(0, 350);
    shapePanel.setBorder(BorderFactory.createTitledBorder("Visual Shape"));
   
    shapePanel.add(showShapeButton);
    
    JComboBox shapeActionCombobox = new JComboBox(new String[] { 
    	"table type", "table size", "critical", "repair", "traffic" });
    shapeActionCombobox.setSelectedIndex(0);
    shapeActionCombobox.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        JComboBox cb = (JComboBox) e.getSource();
        String action = (String) cb.getSelectedItem();
        if (action.equals("table type")){
            nodeShapeAction.setDataField("type");
        } else if (action.equals("table size")){
            nodeShapeAction.setDataField("lines");
        } else if (action.equals("critical")){
          nodeShapeAction.setDataField("critical");
        } else if (action.equals("repair")){
          nodeShapeAction.setDataField("repair");
        } else if (action.equals("traffic")){
          nodeShapeAction.setDataField("traffic");
        }
        m_vis.run("draw");
      }
    });   
    shapePanel.add(shapeActionCombobox);    
    
    // Label and action on node over 
    showSearchPanel.add(searcher);
    showSearchPanel.add(labelsCombobox);
    showSearchPanel.add(showToolTipsButton);     
    showSearchPanel.add(layoutLabel);
    showSearchPanel.add(layoutCombo);
    showSearchPanel.add(distortionLabel);
    showSearchPanel.add(distortionCombo);
    showSearchPanel.add(showArrowButton);
    showSearchPanel.add(showAggregateButton);
    
    // Tooltips
    String tt[] = { "Table name: ", "Line number: ", "Attributes: "};
    String ff[] = { "name", "lines", "attributes"};

    toolTips = new DemoControlToolTip(tt, ff);
    display.addControlListener(toolTips);
    toolTips.setOff();
    
    // overview display
    Display overview = new Display(m_vis);
    overview.setSize(290, 290);
    overview.addItemBoundsListener(new FitOverviewListener());
    overview.addControlListener(toolTips);
    miniDisplayPanel.add(overview);
    
    distancePanel.add(cf);
   
    this.add(showSearchPanel);
    this.add(distancePanel);
    this.add(weightPanel);
    this.add(shapePanel);
    this.add(miniDisplayPanel);   

    setSize(300, 700);
    setVisible(true);
    
    
    //Action encoding
    draw = new ActionList();
    
    draw.add(filter);
    
    ItemAction nodeColor = new NodeColorAction(nodes);
    ItemAction textColor = new TextColorAction(nodes);

    draw.add(nodeColor);
    draw.add(textColor);
    
    // Size/Weigh encoding
    
    draw.add(dataSizeAction);
    draw.add(nodeShapeAction);
    
    int[] palette = new int[] {
        ColorLib.rgba(255,200,200,150),
        ColorLib.rgba(200,255,200,150),
        ColorLib.rgba(200,200,255,150)
    };
    aFill = new DataColorAction(NakeDB.AGGR, "id",
            Constants.NOMINAL, VisualItem.FILLCOLOR, palette);
    aStroke = new ColorAction(NakeDB.AGGR, VisualItem.STROKECOLOR);
    aStroke.setDefaultColor(ColorLib.gray(200));
    aStroke.add("_hover", ColorLib.rgb(255,100,100));
    
   draw.add(aFill);
   draw.add(aStroke);
    
    // Change Stroke Color
    nodeStrokeColor = new ColorAction(nodes, VisualItem.STROKECOLOR, ColorLib.rgb(0,0,100));
    ColorAction edgeStrokeColor = new ColorAction(edges, VisualItem.STROKECOLOR, ColorLib.rgb(0,0,100));      
    ColorAction edgeFillColor = new ColorAction(edges, VisualItem.FILLCOLOR, ColorLib.rgb(200, 200, 200)); 
    
    draw.add(nodeStrokeColor);
    draw.add(edgeStrokeColor);
    draw.add(edgeFillColor);
    
    //Change Stroke Thickness
    StrokeAction nodeStrokeSize = new StrokeAction(nodes, new BasicStroke(1.0f));
    StrokeAction edgeStrokeSize = new StrokeAction(edges, new BasicStroke(2.0f));

    draw.add(nodeStrokeSize);
    draw.add(edgeStrokeSize);
    
    ItemAction edgeColor = new ColorAction(edges, VisualItem.STROKECOLOR,
        ColorLib.rgb(200, 200, 200));
    draw.add(edgeColor);
  
    m_vis.putAction("textColor", textColor);
    m_vis.putAction("draw", draw);
    
  }
  
  public void itemStateChanged(ItemEvent e) {
        Object source = e.getItemSelectable();
    
    if(source == showNameButton) {
      if(showNameButton.isSelected()){
        setGraph("name");
        showName = true;
      }else{
    	setGraph("dummy");
    	showName = false;     
      }     
    }
    
    if(source == showToolTipsButton) {
      if (showToolTipsButton.isSelected()) {
        toolTips.setOn();
      } else {
        toolTips.setOff();
      }
    }    
    
    if(source == showShapeButton) {
       labelRenderer.activateShape(showShapeButton.isSelected());  
    }  
    
    if(source == showSizeButton) {
      if (showSizeButton.isSelected()) {
        dataSizeAction.setMaximumSize(wslider.getValue().doubleValue());
      } else {
        dataSizeAction.setMaximumSize(1);        
      }
      m_vis.run("draw");
     }
    
    if(source == showArrowButton) {
      if (showArrowButton.isSelected()) {
        m_edgeRenderer.setArrowHeadSize(12, 8);
      } else {
        m_edgeRenderer.setArrowHeadSize(0, 0);
      }
    } 
    
    if(source == showAggregateButton) {
      for (AggregateItem item : aggregateItems) {
        item.setVisible(showAggregateButton.isSelected());
      }
    }     
    
  }
  
   /**
   * Set node fill colors
   */
  public static class NodeColorAction extends DataColorAction {
    public NodeColorAction(String group) {
      super(group, NakeDB.group,
        Constants.NOMINAL, VisualItem.FILLCOLOR, new int[] {
    		  ColorLib.rgb(255,180,180), ColorLib.rgb(190,190,255), ColorLib.rgb(200,250,255), ColorLib.rgb(240,250,200), ColorLib.rgb(252,193,134), ColorLib.rgb(200,154,212),  ColorLib.rgb(240,230,194)
        });

      add("_hover", ColorLib.gray(220, 230));
      add("ingroup('_search_')", ColorLib.rgb(55, 190, 190)); 
      add(VisualItem.HIGHLIGHT, ColorLib.rgb(155, 210, 110));   
    }
  }
  
  public static class NodeSizeAction extends DataSizeAction {
    public NodeSizeAction(String group, String field) {
      super(group, field);  
      add("ingroup('_search_')", 2);
    }
  }
  
  /**
   * Set node text colors
   */
  public static class TextColorAction extends ColorAction {
    public TextColorAction(String group) {
      super(group, VisualItem.TEXTCOLOR, ColorLib.gray(0));
      add("_hover", ColorLib.rgb(255, 0, 0));
    }
  }
  
  /**
   * Set node text colors
   */
  public static class NodeShapeAction extends DataShapeAction {
    public NodeShapeAction(String group, String field) {
      super(group, field, 
          new int[] { Constants.SHAPE_RECTANGLE, Constants.SHAPE_DIAMOND,
          Constants.SHAPE_CROSS, Constants.SHAPE_STAR, Constants.SHAPE_ELLIPSE,
          Constants.SHAPE_HEXAGON, Constants.SHAPE_TRIANGLE_UP});
    }
  }  
  
  public void resetDistortionBox() {
	  distortionCombo.setSelectedIndex(0);
  }
  
  public void setGraph(String label) {
	  	Graph graph = null;
	    // update labeling
	    DefaultRendererFactory drf = (DefaultRendererFactory) m_vis.getRendererFactory();
	    ((LabelRenderer) drf.getDefaultRenderer()).setTextField(label);

	    // update graph
	    m_vis.removeGroup(g);
	    VisualGraph vg = m_vis.addGraph(g, graph);
	    m_vis.setValue(e, null, VisualItem.INTERACTIVE, Boolean.FALSE);
	    VisualItem f = (VisualItem) vg.getNode(0);
	    m_vis.getGroup(Visualization.FOCUS_ITEMS).setTuple(f);
	    f.setFixed(false);
	  }

  public NodeSizeAction getDataSizeAction() {
    return dataSizeAction;
  }

  public ActionList getDraw() {
    return draw;
  }
 
}